--
-- user_app.vhd - user application code
--
-- SYNTHESIZABLE
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;
use ieee.std_logic_arith.all;

library unisim;
use unisim.vcomponents.all;

library work;
--use work.memif.all;
use work.admxrc5t2_common.all;
use work.user_defs.all;

entity user_app is
    port(
        rst           : in    std_logic;   -- Reset from memory clock domain
        clk           : in    std_logic;   -- Clock from memory clock domain
        -- To/from local bus interface (32 x 64-bit register)
        reg_in        : in    std_logic_vector(63 downto 0);
        reg_wr        : in    std_logic_vector(255 downto 0); -- 256 bwe
        reg_out       : out   std_logic_vector(2047 downto 0); -- 256 bytes
        -- MGT interface
        mgt_clk       : in    std_logic;
        mgt_rdy       : in    std_logic;   -- MGT clock stable
        mgt_txd       : out   mgt_data_t(3 downto 0);
        mgt_txk       : out   std_logic_vector(7 downto 0);
        mgt_rxd       : in    mgt_data_t(3 downto 0);
        mgt_rxk       : in    std_logic_vector(7 downto 0);
        mgt_aligned   : in    std_logic_vector(3 downto 0);
        -- To/from memory banks
        valid         : in    control_vector_t(max_num_bank - 1 downto 0);
        q             : in    data_vector_t(max_num_bank - 1 downto 0);
        qtag          : in    tag_vector_t(max_num_bank - 1 downto 0);
        ready         : in    control_vector_t(max_num_bank - 1 downto 0);
        req           : out   control_vector_t(max_num_bank - 1 downto 0);
        ce            : out   control_vector_t(max_num_bank - 1 downto 0);
        w             : out   control_vector_t(max_num_bank - 1 downto 0);
        a             : out   address_vector_t(max_num_bank - 1 downto 0);
        tag           : out   tag_vector_t(max_num_bank - 1 downto 0);
        d             : out   data_vector_t(max_num_bank - 1 downto 0);
        be            : out   be_vector_t(max_num_bank - 1 downto 0));
end entity;

architecture syn of user_app is

  type state_type is (ST_IDLE, ST_READ, ST_WRITE, ST_WAIT);
  signal state : state_type;
  signal state_v : std_logic_vector(1 downto 0);

  signal addr : std_logic_vector(max_address_width-1 downto 0);
  signal ce_0, ce_1 : std_logic;
  signal req_i : std_logic;

  signal usr_txdat  : mgt_data_t(3 downto 0);
  signal usr_txwen  : std_logic_vector(3 downto 0);
  signal usr_txrdy  : std_logic_vector(3 downto 0);
  signal usr_rxdat  : mgt_data_t(3 downto 0);
  signal usr_rxrdy  : std_logic_vector(3 downto 0);

  signal usr_tx_cnt : std_logic_vector(15 downto 0);
  signal usr_rx_cnt : std_logic_vector(15 downto 0);
  signal usr_tx_end : std_logic;
  signal usr_rx_err : std_logic;
  signal usr_rx_reg : std_logic_vector(15 downto 0);

begin

  ---------- Memory Interface ----------
  gen_dummy: for i in 2 to max_num_bank -1 generate
    req(i) <= '0';
    ce(i)  <= '0';
    w(i)   <= '0';
    a(i)   <= (others => '0');
    tag(i) <= (others => '0');
    d(i)   <= (others => '0');
    be(i)  <= (others => '0');
  end generate;
  req(0) <= req_i;
  req(1) <= req_i;
  ce(0) <= ce_0 and ready(0);
  ce(1) <= ce_1 and ready(1);
  w(0) <= '0';
  w(1) <= '1';
  a(0) <= addr;
  a(1) <= addr;
  tag(0) <= (others => '0');
  tag(1) <= (others => '0');
  d(0) <= (others => '0');
  U_Q: process (clk)
  begin
    if rising_edge(clk) then
      if valid(0) = '1' then
        d(1) <= q(0);
      end if;
    end if;
  end process;
  be(0) <= (others => '1');
  be(1) <= (others => '1');

  U_FSM: process (clk, rst)
  begin
    if rst = '1' then
      addr <= (others => '0');
      req_i <= '0';
      ce_0 <= '0';
      ce_1 <= '0';
      state <= ST_IDLE;
    elsif rising_edge(clk) then
      case state is

        when ST_IDLE =>
          if reg_wr(0) = '1' then
            req_i <= '1';
          end if;
          if ready(0) = '1' and req_i = '1' then
            ce_0 <= '1';
            state <= ST_READ;
          end if;

        when ST_READ =>
          ce_0 <= '0';
          if valid(0) = '1' then
            ce_1 <= '1';
            state <= ST_WRITE;
          end if;

        when ST_WRITE =>
          if ready(1) = '1' then
            ce_1 <= '0';
            if addr(19 downto 0) = X"20000" then -- 2MB in 128-bit word
              req_i <= '0';
              state <= ST_IDLE;
            else
              ce_0 <= '1';
              addr <= addr + 1;
              state <= ST_WAIT;
            end if;
          end if;

        when ST_WAIT =>
          if ready(0) = '1' then
            ce_0 <= '0';
            state <= ST_READ;
          end if;

      end case;
    end if;
  end process;


  ---------- MGT Interface ----------
  U_MGT : mgt_stream port map (
            rst => rst,
            mgt_clk => mgt_clk,
            mgt_rdy => mgt_rdy,
            mgt_txd => mgt_txd,
            mgt_txk => mgt_txk,
            mgt_rxd => mgt_rxd,
            mgt_rxk => mgt_rxk,
            mgt_aligned => mgt_aligned,
            usr_txdat => usr_txdat,
            usr_txwen => usr_txwen,
            usr_txrdy => usr_txrdy,
            usr_rxdat => usr_rxdat,
            usr_rxrdy => usr_rxrdy);

  -- TX
  usr_txwen(0) <= '0'; usr_txdat(0) <= (others => '0');
  usr_txwen(1) <= '0'; usr_txdat(1) <= (others => '0');
  usr_txwen(2) <= '0'; usr_txdat(2) <= (others => '0');
  -- sending 2-byte data to the East port
  usr_txwen(3) <= usr_txrdy(3) and not usr_tx_end;
  usr_txdat(3) <= usr_tx_cnt;

  U_tx_cnt : process (mgt_clk)
  begin
    if rising_edge(mgt_clk) then
      if rst = '1' then
        usr_tx_cnt <= (others => '0');
      elsif usr_txwen(3) = '1' then
        usr_tx_cnt <= usr_tx_cnt + 1;
      end if;
    end if;
  end process;
  usr_tx_end <= and_reduce(usr_tx_cnt(9 downto 0));

  -- RX
  U_rx_cnt : process (mgt_clk)
  begin
    if rising_edge(mgt_clk) then
      if rst = '1' then
        usr_rx_cnt <= (others => '0');
        usr_rx_reg <= (others => '0');
      elsif usr_rxrdy(0) = '1' then
        usr_rx_cnt <= usr_rx_cnt + 1;
        usr_rx_reg <= usr_rxdat(0);
      end if;
    end if;
  end process;
  U_rx_chk : process (mgt_clk)
  begin
    if rising_edge(mgt_clk) then
      if rst = '1' then
        usr_rx_err <= '0';
      elsif usr_rxrdy(0) = '1' and  usr_rxdat(0) /= usr_rx_cnt then
        usr_rx_err <= '1';
      end if;
    end if;
  end process;

  ---------- User Registers ----------
  state_v <= "00" when state = ST_IDLE else
             "01" when state = ST_READ else
             "10" when state = ST_WRITE else
             "11" when state = ST_WAIT;

  reg_out(0) <= rst;
  reg_out(1) <= reg_wr(0);
  reg_out(2) <= reg_in(0);
  reg_out(3) <= req_i;
  reg_out(4) <= ready(0);
  reg_out(5) <= ready(1);
  reg_out(6) <= state_v(0);
  reg_out(7) <= state_v(1);
  reg_out(8) <= ce_0;
  reg_out(9) <= ce_1;
  reg_out(10) <= '0';
  reg_out(11) <= '0';
  reg_out(31 downto 12) <= addr(19 downto 0);

  reg_out(32) <= usr_rx_err;
  reg_out(33) <= mgt_rdy;
  reg_out(34) <= '0';
  reg_out(35) <= '0';
  reg_out(39 downto 36) <= mgt_aligned;
  reg_out(43 downto 40) <= usr_txrdy;
  reg_out(47 downto 44) <= usr_rxrdy;
  reg_out(63 downto 48) <= usr_tx_cnt;

  reg_out(95 downto 64) <= usr_rx_cnt & usr_rx_reg;

  reg_out(2047 downto 96) <= (others=>'0');

end architecture;
